03. Request Permission

KOTLIN PART2 L4 A03 Request Permissions

Add permissions to the AndroidManifest

The Geofencing API requires location to be shared at all times. If you are on Q or later, you will need to specifically ask the user for it. You will now add in the location permissions in the AndroidManifest.xml:

  1. Add in permissions for ACCESS_FINE_LOCATION and ACCESS_BACKGROUND_LOCATION permission above the application tag.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

Check if the device is running Q (API 29) or later

The difference in location permissions only affects Android Q or later, so the background location permission is only needed for those devices.

  1. In HuntMainActivity.kt, add a member variable above the onCreate() method called runningQOrLater. This will check what API the device is running.
private val runningQOrLater = android.os.Build.VERSION.SDK_INT >= 
android.os.Build.VERSION_CODES.Q

Create method to check for permissions

In your app, you will need to check if permissions are granted, and if they are not, ask for the correct permissions.

  1. In HuntMainActivity, replace the code in the foregroundAndBackgroundLocationPermissionApproved method with this. Each step will be explained in the bullet points below:
@TargetApi(29)
private fun foregroundAndBackgroundLocationPermissionApproved(): Boolean {
   val foregroundLocationApproved = (
           PackageManager.PERMISSION_GRANTED ==
           ActivityCompat.checkSelfPermission(this,
               Manifest.permission.ACCESS_FINE_LOCATION))
   val backgroundPermissionApproved =
       if (runningQOrLater) {
           PackageManager.PERMISSION_GRANTED ==
           ActivityCompat.checkSelfPermission(
               this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
           )
       } else {
           true
       }
   return foregroundLocationApproved && backgroundPermissionApproved
}
  • First, check if the ACCESS_FINE_LOCATION permission is granted.
val foregroundLocationApproved = (
           PackageManager.PERMISSION_GRANTED ==
           ActivityCompat.checkSelfPermission(this,
               Manifest.permission.ACCESS_FINE_LOCATION))
  • If the device is running Q or higher, check that the ACCESS_BACKGROUND_LOCATION permission is granted. Return true if the device is running lower than Q where you don't need a permission to access location in the background.
val backgroundPermissionApproved =
   if (runningQOrLater) {
       PackageManager.PERMISSION_GRANTED ==
       ActivityCompat.checkSelfPermission(
           this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
       )
   } else {
       true
   }

*Return true if the permissions are granted and false if not.

return foregroundLocationApproved && backgroundPermissionApproved

Request permissions

  1. Copy this code into the requestForegroundAndBackgroundLocationPermissions() method, This is where you will ask the user to grant location permissions. Each step will be explained in the bullet points below:
@TargetApi(29 )
private fun requestForegroundAndBackgroundLocationPermissions() {
   if (foregroundAndBackgroundLocationPermissionApproved())
       return
   var permissionsArray = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
   val resultCode = when {
       runningQOrLater -> {
           permissionsArray += Manifest.permission.ACCESS_BACKGROUND_LOCATION
           REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE
       }
       else -> REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE
   }
   Log.d(TAG, "Request foreground only location permission")
   ActivityCompat.requestPermissions(
       this@HuntMainActivity,
       permissionsArray,
       resultCode
   )
}
  • If the permissions have already been approved, you don’t need to ask again. Return out of the method.
if (foregroundAndBackgroundLocationPermissionApproved())
   return
  • The permissionsArray contains the permissions that are going to be requested. Initially, add ACCESS_FINE_LOCATION since that will be needed on all API levels.
var permissionsArray = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
  • Below it, you will need a resultCode. The code will be different depending on if the device is running Q or later and will inform us if you need to check for one permission (fine location) or multiple permissions (fine and background location) when the user returns from the permission request screen. Add a when statement to check the version running and assign result code to REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE if the device is running Q or later and REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE if not.
val resultCode = when {
   runningQOrLater -> {
       permissionsArray += Manifest.permission.ACCESS_BACKGROUND_LOCATION
       REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE
   }
   else -> REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE
}
  • Request permissions passing in the current activity, the permissions array and the result code.
ActivityCompat.requestPermissions(
   this@HuntMainActivity,
   permissionsArray,
   resultCode
)

Handle permissions

Once the user responds to the permissions, you will need to handle their response using the onRequestPermissionsResult().

  1. Copy this code in under the:
override fun onRequestPermissionsResult(
   requestCode: Int,
   permissions: Array<String>,
   grantResults: IntArray
) {
   Log.d(TAG, "onRequestPermissionResult")

   if (
       grantResults.isEmpty() ||
       grantResults[LOCATION_PERMISSION_INDEX] == PackageManager.PERMISSION_DENIED ||
       (requestCode == REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE &&
               grantResults[BACKGROUND_LOCATION_PERMISSION_INDEX] ==
               PackageManager.PERMISSION_DENIED))
   {
       Snackbar.make(
           binding.activityMapsMain,
           R.string.permission_denied_explanation, 
           Snackbar.LENGTH_INDEFINITE
       )
           .setAction(R.string.settings) {
               startActivity(Intent().apply {
                   action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
                   data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
                   flags = Intent.FLAG_ACTIVITY_NEW_TASK
               })
           }.show()
   } else {
       checkDeviceLocationSettingsAndStartGeofence()
   }
}
  • Permissions can be denied in a few ways:
    • If the grantResults array is empty, then the interaction was interrupted and the permission request was cancelled.
    • If the grantResults array’s value at the LOCATION_PERMISSION_INDEX has a PERMISSION_DENIED it means that the user denied foreground permissions.
    • If the request code equals REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE and the BACKGROUND_LOCATION_PERMISSION_INDEX is denied it means that the device is running API 29 or above and that background permissions were denied.
if (grantResults.isEmpty() ||
   grantResults[LOCATION_PERMISSION_INDEX] == PackageManager.PERMISSION_DENIED ||
   (requestCode == REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE &&
           grantResults[BACKGROUND_LOCATION_PERMISSION_INDEX] ==
           PackageManager.PERMISSION_DENIED))
  • This app has very little use when permissions are not granted so present a snackbar explaining that the user needs location permissions in order to play.
Snackbar.make(
   binding.activityMapsMain,
   R.string.permission_denied_explanation,
   Snackbar.LENGTH_INDEFINITE
)
   .setAction(R.string.settings) {
       startActivity(Intent().apply {
           action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
           data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
           flags = Intent.FLAG_ACTIVITY_NEW_TASK
       })
   }.show()
  • If not, permissions have been granted! Call the checkDeviceLocationSettingsAndStartGeofence() method!
else {
   checkDeviceLocationSettingsAndStartGeofence()
}
  1. Run the app! You will see a pop up prompting you to grant permissions. Choose Allow all the time or Allow if you are running an API lower than 29.